package br.com.arquitetura.data;

import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.SimpleExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import br.com.arquitetura.entidade.Persistente;
import br.com.arquitetura.excecoes.*;

/**
 * Classe respons�vel por concentrar todos comportamentos comuns as entidades que far�o acesso aos dados.
 * 
 * @author M�rio Melo
 *
 */
@Repository(value="genericDao")
public class GenericDao extends HibernateDaoSupport{
	
	private JdbcTemplate template;
	
	@Autowired
	public GenericDao(SessionFactory sessionFactory) {
		setSessionFactory(sessionFactory);
	}
	
	public GenericDao(){};

	public <T extends Persistente> T getById(final Class<T> classe, final Long id)  {
		try {
			T objeto = getHibernateTemplate().get(classe, id);			
			return objeto;
		} catch (HibernateException e) {
			throw new RuntimeException(e);
		}
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T extends Persistente> List<T> getByField(final Class<T> classe, final String campo, final Object value)  {
		return (List<T>) this.getHibernateTemplate().execute(
				new HibernateCallback() {

					// O HibernateCallback possibilita o acesso direto � API do
					// Hibernate para a execu��o de consultas mais complexas que
					// n�o s�o cobertas pelo HibernateTemplate
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						Criteria c = session.createCriteria(classe);
						
						c.add(Restrictions.eq(campo, value));
						
						c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);						

						return c.list();
					}

				});
	}
	
	public void addEntity(final Persistente objeto) throws BDException{
		this.getHibernateTemplate().clear(); 
		this.getHibernateTemplate().saveOrUpdate(objeto);
		this.getHibernateTemplate().flush();
	}

	public void updateEntity(final Persistente objeto) throws BDException{
		this.getHibernateTemplate().clear(); 
		this.getHibernateTemplate().update(objeto);
	}

	public void removeEntity(Persistente objeto) throws BDException{
		this.getHibernateTemplate().delete(objeto);
		//this.getHibernateTemplate().refresh(objeto);
		this.getHibernateTemplate().flush();
	}

	public <T extends Persistente> List<T> listAll(final Class<T> classe){
		return this.getHibernateTemplate().loadAll(classe);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T extends Persistente> List<T> listOrderedByField(final Class<T> classe, final String orderBy,
			final String ascDesc)  {
		return (List<T>) this.getHibernateTemplate().execute(
				new HibernateCallback() {

					// O HibernateCallback possibilita o acesso direto � API do
					// Hibernate para a execu��o de consultas mais complexas que
					// n�o s�o cobertas pelo HibernateTemplate
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						Criteria c = session.createCriteria(classe);
						if ("desc".equals(ascDesc))
							c.addOrder(Order.desc(orderBy));
						else
							c.addOrder(Order.asc(orderBy));
						
						c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);						

						return c.list();
					}

				});
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <T extends Persistente> List<T> buscarTodosAtivos(final Class<T> classe, final String orderBy,
			final String ascDesc)  {
		return (List<T>) this.getHibernateTemplate().execute(
				new HibernateCallback() {

					// O HibernateCallback possibilita o acesso direto � API do
					// Hibernate para a execu��o de consultas mais complexas que
					// n�o s�o cobertas pelo HibernateTemplate
					public Object doInHibernate(Session session)
							throws HibernateException, SQLException {
						Criteria c = session.createCriteria(classe);
						String ordens[] = orderBy.split(",");
						for(String ordem : ordens){
							if ("desc".equals(ascDesc))
								c.addOrder(Order.desc(ordem));
							else
								c.addOrder(Order.asc(ordem));
						}
						
						c.add(Restrictions.eq("ativo", true));
						
						c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);						

						return c.list();
					}

				});
	}
	
	

	public void flush() {
		this.getHibernateTemplate().flush();
		
	}
	
	protected SimpleExpression eq(String propriedade,Object valor) throws HibernateException{
		return Restrictions.eq(propriedade, valor);
	}
	protected Order asc(String propriedade) throws HibernateException{
		return Order.asc(propriedade);
	}
	
	protected Criteria getCriteria(Class< ? extends Persistente> classe){
		return getSession().createCriteria(classe);
	}
	
	protected Restrictions ilike(String propriedade,Object valor){
		return (Restrictions) Restrictions.ilike(propriedade,valor);
	}
	
	protected JdbcTemplate getJdbcTemplate(DataSource dataSource){
		if(template == null)
			template = new JdbcTemplate(dataSource);
		
		return template;
	}
	
	public JdbcTemplate getJdbcTemplate(){
		if(template == null)
			template = new JdbcTemplate();
		
		return template;
	}
}
